﻿using System.Collections;
using ApiConceptsFramework.Contract;
using ApiConceptsFramework.FluentApi;
using NUnit.Framework;
using System.Collections.Generic;
using System.Linq;
using FluentAssertions;

namespace test.ApiConceptsFramework.FluentApi
{
	[TestFixture]
    public class ApiEvaluatorTests
	{
// ReSharper disable UnusedMember.Local
// ReSharper disable UnusedParameter.Local
		class TestClass
		{
			public TestClass(string p1) { }
			public TestClass(bool p1) { }
			public void Method1() { }
			public void Method2() { }
			public static void StaticMethod() { }
			public void MethodWithManyParams(int p1, int p2, int p3, int p4, int p5) { }
			public int Field1 = 0;
			public int Field2 = 0;
			public static int StaticField = 0;
			public int Property1 { get; set; }
// ReSharper disable UnusedAutoPropertyAccessor.Local
			public int Property2 { get; set; }
// ReSharper restore UnusedAutoPropertyAccessor.Local
		}
// ReSharper restore UnusedMember.Local
// ReSharper restore UnusedParameter.Local

		private ApiEvaluator _eval;

		[SetUp]
		public void SetUp()
		{
			_eval = new ApiEvaluator();
		}

		[Test]
		public void ClassUsage_with_nongeneric_method()
		{
			//Act
			_eval.ClassUsage(typeof(TestClass));

			//Assert
			_eval.Data.Should().HaveCount(1);
			var data = _eval.Data.First();
			data.Concept.Should().Be(Concepts.ClassUsage);
			data.Target.Type.Should().Be(typeof(TestClass));
			data.Target.Member.Should().BeNull();
		}

		[Test]
		public void Instantiation_is_correctly_added_to_data()
		{
			//Arrange
			var ctor = typeof (TestClass).GetConstructor(new[] {typeof (string)});

			//Act
			_eval.Instantiation(() => new TestClass("test"));

			//Assert
			_eval.Data.Should().HaveCount(2);
			var data = _eval.Data.Last();
			data.Concept.Should().Be(Concepts.Instantiation);
			data.Target.Type.Should().Be(typeof(TestClass));
			data.Target.Member.Should().Be(ctor);
		}

		[Test]
		public void MethodCall_is_correctly_added_to_data()
		{
			//Arrange
			var method = typeof (TestClass).GetMethod("Method2");

			//Act
			_eval.MethodCall<TestClass>(_ => _.Method2());

			//Assert
			_eval.Data.Should().HaveCount(1);
			var data = _eval.Data.First();
			data.Concept.Should().Be(Concepts.MethodCall);
			data.Target.Type.Should().Be(typeof(TestClass));
			data.Target.Member.Should().Be(method);
		}

		[Test]
		public void MethodCall_static()
		{
			//Arrange
			var method = typeof(TestClass).GetMethod("StaticMethod");

			//Act
			_eval.MethodCall(() => TestClass.StaticMethod());

			//Assert
			_eval.Data.Should().HaveCount(2);
			var data = _eval.Data.Last();
			data.Concept.Should().Be(Concepts.MethodCall);
			data.Target.Type.Should().Be(typeof(TestClass));
			data.Target.Member.Should().Be(method);
		}

		[Test]
		public void FieldAccess_is_correctly_added_to_data()
		{
			//Arrange
			var field = typeof(TestClass).GetField("Field1");

			//Act
			_eval.FieldAccess((TestClass _) => _.Field1);

			//Assert
			_eval.Data.Should().HaveCount(1);
			var data = _eval.Data.First();
			data.Concept.Should().Be(Concepts.FieldAccess);
			data.Target.Type.Should().Be(typeof(TestClass));
			data.Target.Member.Should().Be(field);
		}

		[Test]
		public void FieldAccess_static()
		{
			//Arrange
			var field = typeof(TestClass).GetField("StaticField");

			//Act
			_eval.FieldAccess(() => TestClass.StaticField);

			//Assert
			_eval.Data.Should().HaveCount(2);
			var data = _eval.Data.Last();
			data.Concept.Should().Be(Concepts.FieldAccess);
			data.Target.Type.Should().Be(typeof(TestClass));
			data.Target.Member.Should().Be(field);
		}

		[Test]
		public void PropertyAccess_is_correctly_added_to_data()
		{
			//Arrange
			var property = typeof(TestClass).GetProperty("Property2");

			//Act
			_eval.PropertyAccess((TestClass _) => _.Property2);

			//Assert
			_eval.Data.Should().HaveCount(1);
			var data = _eval.Data.First();
			data.Concept.Should().Be(Concepts.FieldAccess);
			data.Target.Type.Should().Be(typeof(TestClass));
			data.Target.Member.Should().Be(property);
		}

		[Test]
		public void PropertyAccess_of_indexer()
		{
			//Arrange
			var property = typeof(ArrayList).GetProperties().FirstOrDefault(p => p.GetIndexParameters().Length > 0);

			//Act
			_eval.PropertyAccess((ArrayList list) => list[0]);

			//Assert
			_eval.Data.Should().HaveCount(1);
			var data = _eval.Data.First();
			data.Concept.Should().Be(Concepts.FieldAccess);
			data.Target.Type.Should().Be(typeof(ArrayList));
			data.Target.Member.Should().Be(property);
		}

		[Test, Explicit]
		public void EvaluateAndPrintToConsole()
		{
			_eval.IDE = IDE.VisualStudioOrSimilar;

			//_eval.ClassUsage<TestClass>();
			_eval.Instantiation(() => new TestClass("test"));
			_eval.MethodCall((TestClass _) => _.MethodWithManyParams(1, 2, 3, 4, 5));
			_eval.FieldAccess((TestClass _) => _.Field1);
			_eval.PropertyAccess((TestClass _) => _.Property2, 2);

			_eval.EvaluateAndPrintToConsole();
		}
    }
}
